import pyperclip as pc
import curses
import sys

sys.path.append("..")
from src.constants import *
from ui.constants import *

from src.vobjects.channel import Channel
from commands.core.commander import Commander

MODE_LIST = "List mode"
MODE_SEARCH = "Search mode"
MODE_CMD = "Cmd mode"

class LinePicker:

    def __init__(self, title='', items=[], index: int=0, indent: int=3):
        self.title = title
        self.items = items

        self.current_items = items
        
        self.mode           = MODE_LIST
        self.search_query   = ""
        self.cmd_text       = ""
        self.cmd            = ""

        self.commander          = Commander()
        self.is_stoped          = False
        self.is_watched_hidden  = False

        self.index              = index
        self.scroll_top         = 0
        self.indent             = indent

    def toggle_cmd(self):
        if self.mode == MODE_LIST: 
            curses.curs_set(1)
            self.mode = MODE_CMD
        else: 
            curses.curs_set(0)
            self.mode = MODE_LIST

    def toggle_search(self):
        if self.mode == MODE_LIST: 
            curses.curs_set(1)
            self.mode = MODE_SEARCH
        else: 
            curses.curs_set(0)
            self.mode = MODE_LIST

    def move_up(self):
        self.index -= 1
        if self.index < 0:
            self.index = len(self.current_items) - 1

    def move_down(self):
        self.index += 1
        if self.index >= len(self.current_items):
            self.index = 0

    def clear_picked_items(self):
        for item in self.items:
            item.is_picked = False

    def get_picked_items(self):
        items = list()
        for item in self.items:
            if item.is_picked: items.append(item)
        return items

    def pick_current_item(self):
        item = self.items[self.index]
        if item.is_picked:
            item.is_picked = False
        else: item.is_picked = True

    def get_selected(self):
        """Return current item from items list"""
        items = self.get_picked_items()
        if len(items) > 0: 
            return items, 0

        ln = len(self.current_items)
        if ln >= self.index and ln > 0: return self.current_items[self.index], self.index
        else: return None, -1

    def crop_line_to_limit(self, line, limit):    
        if len(line) > limit:
            line = line[:limit-3] + '...'
        return line 

    def is_items_has_subtitle(self):
        for item in self.items: 
            if item.subtitle != "": return True
        return False

    def get_visible_items(self, items: list=[], max_rows: int=0):
        start = self.scroll_top
        end = self.scroll_top + max_rows
        cline = self.index + 1

        sdelim = self.indent - (cline - start - 1)
        edelim = self.indent - (end - cline)

        if sdelim > 0: start -= sdelim; end -= sdelim
        if edelim > 0: end += edelim; start += edelim

        if start < 0: start = 0; end = max_rows
        if end > len(items): end = len(items); start = end - max_rows
        if start < 0: start = 0

        return items[start : end], start

    def draw_title(self, screen):
        y, x = 0, 0 # start point
        max_y, max_x = screen.getmaxyx()

        self.clear_line(screen, y, max_x, self.OPT_COLOR)

        title = " " + self.title

        screen.addstr(y, x, title, self.OPT_COLOR)

        return y+1

    def draw_body(self, screen, y, x): 
        max_y, max_x = screen.getmaxyx()
        middle_y, middle_x = int(max_y/2), int(max_x/2)

        max_line_length     = max_x - 2
        max_subtitle_len    = 4
        max_title_len       = max_line_length - 61
        max_fstr_len        = 10
        max_sstr_len        = 23
        max_tstr_len        = 20

        max_rows = max_y - y - 2 # max draws we can draw

        # calculate how many line we should scroll, relative to the top
        current_line = self.index + 1
        if current_line <= self.scroll_top:
            self.scroll_top = current_line - 1 
        elif current_line - self.scroll_top > max_rows:
            self.scroll_top = current_line - max_rows

        titems = []
        for item in self.items:
            if self.search_query in item.title.lower():
                if not self.is_watched_hidden or item.subtitle != '[+]':
                    titems.append(item)
        self.current_items = titems

        items_to_draw, start = self.get_visible_items(titems, max_rows)
        self.scroll_top = start

        index = start
        for item in items_to_draw:
            title = self.crop_line_to_limit(item.title, max_title_len)
            fstr = self.crop_line_to_limit(item.fstr, max_fstr_len)
            sstr = self.crop_line_to_limit(item.sstr, max_sstr_len)
            tstr = self.crop_line_to_limit(item.tstr, max_tstr_len)

            tx = x
            if index != self.index: 
                tpair, fpair, spair, tspair = self.BLUE_TEXT, self.YELLOW_TEXT, self.RED_TEXT, self.GREEN_TEXT
                stpair = self.RED_TEXT
            else:
                tpair, fpair, spair, tspair = self.CURRENT_ITEM_COLOR, self.CURRENT_ITEM_COLOR, self.CURRENT_ITEM_COLOR, self.CURRENT_ITEM_COLOR
                stpair = self.CURRENT_ITEM_COLOR #self.CYAN_TEXT
                self.clear_line(screen, y, max_x, self.CURRENT_ITEM_COLOR)
                tx += 1

            if item.is_picked: screen.addnstr(y, 0, '[*]', max_subtitle_len, stpair)
            elif item.subtitle != "": screen.addnstr(y, 0, item.subtitle, max_subtitle_len, stpair)
            
            if item.subtitle == '[+]' and index != self.index: 
                tpair, fpair, tspair = self.CYAN_TEXT, self.CYAN_TEXT, self.CYAN_TEXT

            screen.addnstr(y, tx, title, max_title_len, tpair)
            tx += max_title_len + 1
            screen.addnstr(y, tx, str(fstr), max_fstr_len, fpair)
            tx += max_fstr_len + 1
            screen.addnstr(y, tx, sstr, max_sstr_len, spair)
            tx += max_sstr_len + 1
            screen.addnstr(y, tx, tstr, max_tstr_len, tspair)

            index += 1
            y += 1

        return y

    def draw_status(self, screen):
        max_y, max_x = screen.getmaxyx()
        y, x = max_y-2, 1
        self.clear_line(screen, y, max_x, self.OPT_COLOR)

        item, _ = self.get_selected()
        if type(item) is list: 
            status = f"Picked {len(item)}/{len(self.items)}"
            return screen.addstr(y, x, status, self.OPT_COLOR)

        status = f"[{self.index+1}/{len(self.items)}]"

        if item == None: return

        for key, value in item.status.items():
            status += f" | {key}: {value}"

        screen.addstr(y, x, status, self.OPT_COLOR)

    def clear_line(self, screen, y, l, attr=None):
        for i in range(l): screen.addstr(y, i, ' ', attr) 

    def draw(self, screen):
        # clear screen
        screen.erase()

        y, x = 0, 1 # start point
        if self.is_items_has_subtitle(): x += 3
        max_y, max_x = screen.getmaxyx()

        y = self.draw_title(screen)
        self.draw_body(screen, y, x)
        self.draw_status(screen)

        if self.mode == MODE_SEARCH:
            text = "/" + self.search_query
            screen.addstr(max_y-1, 0, text, self.GREEN_TEXT)
        elif self.mode == MODE_CMD:
            text = ":" + self.cmd
            screen.addstr(max_y-1, 0, text, self.GREEN_TEXT)
        elif self.cmd_text != None:
            splited = self.cmd_text.split(" ")
            first = splited[0]
            screen.addstr(max_y-1, 0, first, self.GREEN_TEXT)
            if len(splited) > 1:
                text = " ".join(splited[1:])
                screen.addstr(max_y-1, len(first)+1, text)

        screen.refresh()

    def copy_current_item_url(self):
        item, index = self.get_selected()
        if "url" in item.status: pc.copy(item.status["url"])

    def add_current_item_to_subs(self):
        item, index = self.get_selected()
        if type(item) is list: item = item[0]
        if type(item.parent) is Channel:
            is_subscribed, _, _ = item.parent.is_subscribed()
            if is_subscribed:
                item.subtitle = ""
                item.parent.unsubscribe()
            else: 
                item.subtitle = SUBSCRIBED_CHANNEL_SUBTITLE
                item.parent.subscribe()
      
    def _exec_cmd(self, screen):
        self.is_stoped = True
        screen.move(0, 0)
        screen.clear()
        curses.echo()
        screen.refresh()

        self.cmd_text = self.commander.execute_command(self.cmd, self)

        curses.noecho()
        screen.clear()
        screen.refresh()
        self.is_stoped = False

    def _search_upd(self, screen, char):
        y, _ = screen.getmaxyx()

        screen.move(y-1, len(self.search_query)+1)

        if char in KEYS_BACKSPACE:
            self.search_query = self.search_query[:-1]
        else:
            self.search_query += char

    def _cmd_upd(self, screen, char):
        y, _ = screen.getmaxyx()

        screen.move(y-1, len(self.cmd)+1)

        if char in KEYS_BACKSPACE:
            self.cmd = self.cmd[:-1]
        else:
            self.cmd += char
   
    def cmd_keys(self, screen, char, char_code):
        if char_code in KEYS_ENTER:
            if self.cmd != "":
                self._exec_cmd(screen=screen)
                self.cmd = ""
            self.toggle_cmd()
        elif char_code in KEYS_DOUBLE_DOTS:
            self.toggle_cmd()
        else:
            self._cmd_upd(screen, char)

        return None, -2

    def search_keys(self, screen, char, char_code):
        if char_code in KEYS_ENTER:
            item, index = self.get_selected()
            self.clear_picked_items()
            return item, index
        elif char_code in KEYS_SEARCH:
            self.toggle_search()
        else:
            self._search_upd(screen, char)
            self.index = 0

        return None, -2

    def default_keys(self, screen, char, char_code):
        if char_code in KEYS_ENTER:
            item, index = self.get_selected()
            self.clear_picked_items()
            return item, index
        elif char_code in KEYS_SEARCH:
            self.toggle_search()
        elif char_code in KEYS_DOUBLE_DOTS:
            self.toggle_cmd()

        elif char_code in KEYS_UP:
            self.move_up()
        elif char_code in KEYS_DOWN:
            self.move_down()
        elif char_code in KEYS_C:
            self.copy_current_item_url()
        elif char_code in KEYS_S:
            self.add_current_item_to_subs()
        elif char_code in KEYS_P:
            self.pick_current_item()
        elif char_code in KEYS_BACKSPACE:
            return '..', -1
        elif char_code in KEYS_Q:
            exit(0)

        return None, -2

    def run_loop(self, screen):
        while True:
            if self.is_stoped:
                continue
                
            self.draw(screen)
            
            # get pressed keys
            char = screen.get_wch()
            char_code = char
            if type(char) is str:
                char_code = ord(char)

            if self.mode == MODE_CMD:
                self.cmd_text = ""
                self.cmd_keys(screen, char, char_code)
            elif self.mode == MODE_SEARCH:
                self.cmd_text = ""
                item, index = self.search_keys(screen, char, char_code)
                if item != None: return item, index
            else:
                item, index = self.default_keys(screen, char, char_code)
                if item != None: return item, index

    def init_curses(self):
        # use the default colors of the terminal
        curses.use_default_colors()

        curses.init_pair(1, curses.COLOR_GREEN, -1)
        curses.init_pair(2, curses.COLOR_MAGENTA, -1)
        curses.init_pair(3, curses.COLOR_RED, -1)
        curses.init_pair(4, curses.COLOR_CYAN, -1)
        curses.init_pair(5, -1, curses.COLOR_BLUE)
        curses.init_pair(6, -1, curses.COLOR_CYAN)
        curses.init_pair(7, curses.COLOR_BLACK, curses.COLOR_GREEN)
        curses.init_pair(8, curses.COLOR_GREEN, curses.COLOR_BLUE)
        curses.init_pair(9, curses.COLOR_BLUE, -1)
        curses.init_pair(10, curses.COLOR_YELLOW, -1)

        self.GREEN_TEXT         = curses.color_pair(1)
        self.MAGENTA_TEXT       = curses.color_pair(2)
        self.RED_TEXT           = curses.color_pair(3)
        self.CYAN_TEXT          = curses.color_pair(4)
        self.BLUE_BG            = curses.color_pair(5)
        self.CYAN_BG            = curses.color_pair(6)
        self.CURRENT_ITEM_COLOR = curses.color_pair(7)
        self.OPT_COLOR          = curses.color_pair(8)
        self.BLUE_TEXT          = curses.color_pair(9)
        self.YELLOW_TEXT        = curses.color_pair(10)

        # hide the cursor
        curses.curs_set(0)

        curses.initscr()

    def _start(self, screen):
        self.init_curses()
        screen.idlok(False)
        screen.idcok(False)
        return self.run_loop(screen)

    def start(self):
        return curses.wrapper(self._start)

def pick(title='', items=[], index: int=0):
    picker = LinePicker(title, items, index=index)
    
    return picker.start()



